﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace SimpleServiceBus.Utilities
{
    /// <summary>
    /// Summary description for TimeDelayedEvent.
    /// </summary>
    public class BufferedOperationAggregator<T>
    {
        private readonly object _threadLock = new object();
        private TimeSpan _delay = TimeSpan.FromMilliseconds(500);
        private Timer _timer;
        private readonly List<T> _userData = new List<T>();
        private TimeSpan _maxDelay = TimeSpan.FromMinutes(1);
        private readonly Stopwatch _monitor = new Stopwatch();

        public Action<List<T>> IdleAction { get; set; }

        public TimeSpan Delay
        {
            get { return _delay; }
            set { _delay = value; }
        }

        public TimeSpan MaxDelay
        {
            get { return _maxDelay; }
            set { _maxDelay = value; }
        }

        public bool HasPendingEvent
        {
            get
            {
                lock (_threadLock)
                {
                    return _timer != null;
                }
            }
        }

        public void SubmitEvent(T userData)
        {
            lock (_threadLock)
            {
                _userData.Add(userData);
                if (_monitor.IsRunning && _monitor.Elapsed >= MaxDelay)
                {
                    PerformIdleAction();
                }
                else
                    ResetTimer();
            }
        }

        private void ResetTimer()
        {
            if (_timer == null)
            {
                _timer = new Timer(TimerTick, null, _delay, _delay);
                _monitor.Start();
            }
            else
            {
                _timer.Change(_delay, _delay);
            }
        }

        private void TimerTick(object state)
        {
            PerformIdleAction();
        }

        private void PerformIdleAction()
        {
            List<T> aggregatedData = null;

            lock (_threadLock)
            {
                _monitor.Stop();
                _monitor.Reset();
                if (_timer != null)
                {
                    _timer.Dispose();
                    _timer = null;
                }

                aggregatedData = new List<T>(_userData);
                _userData.Clear();
            }

            if (IdleAction != null)
                IdleAction(aggregatedData);
        }
    }
}